Link to this headingDiffie–Hellman (DHКЕ)
- It does not authenticate and is vulnerable to MITM attacks
- Uses Discrete Log to make a shared key
- Uses the property that
(g^a)^b mod p = (g^b)^a mod p
- Uses the property that
- Uses the properity that finding the secret
afromg^a mod pis hard
Link to this headingProtocol Example
- User1 and User2 use two public integers:
- P which is the modulus (Where P is prime)
- G which is the base (Where G is repetitively prime to P)
- For Example let
p = 23andg = 5. (These are usually Hard-coded) - User1 chooses a secret integer a (e.g. a = 4)
5^4 mod 23 = 4(This Output is public Data A)
- User2 chooses a secret integer a (e.g. a = 3)
5^3 mod 23 = 10(This Output is public Data B)
- Each User exchanges the output to each other
- User1 Computes the shared secret
10^4 mod 23 = 18
- User2 Computes the shared secret
4^3 mod 23 = 18
In the most common implementation of DHKE (following the RFC 3526) the base is g = 2 and the modulus p is a large prime number (1536 … 8192 bits).
Link to this headingImplementation
#generate a 1024 bit prime number for the Mod
#prime_mod = generate_probable_prime(1024)
=
#Generate Secret Exponent
= 0
=
=
#Generate the Transmit key A = G^x % P
=
return ,
#Genearte User1 Keys
, =
#Generate User2 Keys
, =
#Generate Shared Secret from the other user's Public Key
=
=
assert ==
Link to this headingAttacks
Link to this headingSmall Subgroup Confinement Attack
By choosing a value for k where k = (prime_mod-1)/w and w is the prime of the Small Subgroup. This makes the secret key to be in the small group and can be found through an exhaustive search.
- User1 calculates her public key as
A = g^(x*a)and sends it over the wire - The MITM captures
Aand replaces it withA^k - User2 calculates his public key as
B = g^(y*b)and sends it over the wire- User2 calculates the shared private key as
S = (A^k)^b
- User2 calculates the shared private key as
- The MITM captures
Band replaces it withB^k - User2 calculates the shared private key as
S = (B^k)^a
Example Code:
= 7199773997391911030609999317773941274322764333428698921736339643928346453700085358802973900485592910475480089726140708102474957429903531369589969318716771
= 4565356397095740655436854503483826832136106141639563487732438195343690437606117828318042418238184896212352329118608100083187535033402010599512641674644143
= 236234353446506858198510045061214171961
= 30477252323177606811760882179058908038824640750610513771646768011063128035873508507547741559514324673960576895059570
"""
Extended Euclidean algorithm
:return: ( 'gcd' - the resulting gcd,
'coeffs' - Bézout coefficients )
"""
, = ,
, = 1, 0
, = 0, 1
= //
, = , - *
, = , - *
, = , - *
return ,
"""
Solution of the system:
x = a1 (mod n1)
x = a2 (mod n2)
...
x = ak (mod k)
Such that 0 <= x < N,
where N = n1 * n2 * ... * nk
https://en.wikipedia.org/wiki/Chinese_remainder_theorem#Existence_(direct_construction)
"""
= 0
=
= //
, =
+= **
return %
"""
Basic Integer Factorization Algorithm.
https://en.wikipedia.org/wiki/Trial_division
"""
yield 2
//= 2
= 3
yield
//=
+= 2
yield
=
= b
=
return ,
"""
return element h of order r.
h := rand(1, p)^((p-1)/r) mod p
"""
=
return
#Generate Bob Keys
#Because the generator that we have picked is a small subgroup the order of the subgroup is also smaller.
#This means that the random even if chosen to be larger can be reduced to
=
=
=
=
#The Public Key even though it is bigger than the order will be the same when it is computed
= %
=
# print(f"bob_private_key: {bob_private_key}")
# print(f"bob_private_key2: {bob_private_key2}")
# print(f"bob_private_key3: {bob_private_key3}")
# print(f"bob_public_key: {bob_public_key}")
# print(f"bob_public_key2: {bob_public_key2}")
# print(f"bob_public_key3: {bob_public_key3}")
assert ==
### Attack Start
#Find the order. Amusing the prime_mod is prime then the order is P-1
#If it is not prime then P-1 = cofactor*order
#So we factor P-1 and see if any of the factors are the order
= - 1
#order_factors = factorint(prime_order)
= 1
=
=
# Use the prime_factors_iterator to factor prime_order step by step
#Ignore Repeated factors
continue
*=
# Perform the attack for each factor incrementally
=
# Get Hmac Message with shared key as HMAC Key
, =
# Bruteforce Key
=
=
#print("Found HMAC match")
break
break
# Once all factors are processed, use Chinese remainder theorem
=
Link to this headingSimple Brute Force
#generate a 1024 bit prime number for the Mod
#prime_mod = generate_probable_prime(1024)
=
#Genreate Secret Exponent
= 0
=
=
#Generate the Transmit key A = G^x % P
=
return ,
=
=
return 2
return -1
return 1
= * %
return
return None
#Genearte User1 Keys
, =
=
#[+] Using Brute Force algorithm to solve DLP
#[+] Modulus size: 32. Warning! Brute Force is not efficient
#96057192
#[Finished in 13.8s]
Link to this headingBaby Step Giant Step
- O(sqrt(n))
- Takes alot of memory to run
Any secret can be expressed as x = i*m + j
\begin{aligned}
y &= g^x \pmod{p} \\
&= g^{i*m + j} \pmod{p} \\
&= g^{i*m + j} \pmod{p} \\
&= g^{i*m} * g^j \pmod{p} \\
\end{aligned}
\\~\\
y * g^{-i*m} = g^j \pmod{p}
#generate a 1024 bit prime number for the Mod
#prime_mod = generate_probable_prime(1024)
=
#Genreate Secret Exponent
= 0
=
=
#Generate the Transmit key A = G^x % P
=
return ,
=
=
# Make a lookup table for 1,j
=
# Precompute g^(-m) so it is easy to substitute in every lookup
=
# Giant Steps
#Calculate y*g^(-m*i) = y * g^-m * g^i = y * c * g^i = y * c ^ i
= %
#Check if value is in lookup table and get the index
# x found
return * +
return None
#Generate User1 Keys
, =
#user1_public_key, user1_private_key = generate_key_pair(prime=13091037018084742351)
=
assert ==
#[+] Using BSGS algorithm to solve DLP
#[+] Modulus size: 32. Warning! BSGS not space efficient
#Found exponent: 3238504688
#[Finished in 269ms]
Link to this headingPollards Rho
- O(sqrt(n))
- Fastest for prime orders
f
"""
Pollard Step
:param x:
:param a:
:param b:
:return:
"""
, , , =
= % 3 # Subsets
= * %
= %
= * %
= %
# sub == 2:
= * %
= *2 %
= *2 %
return , ,
# P: prime
# H:
# G: generator
= # multiplicative sub group order
, , = 1, 0, 0
, , = , ,
# for i in range(1, P):
# Hedgehog
, , =
# Hare
, , =
, , =
break
= -
= -
# It is necessary to compute the inverse to properly compute the fraction mod q
# find gcd before solving linear equation denum*res = num mod Q
=
# print(f'gcds = {gcdz}')
# standard solution
= * % # divm(num, denum, Q) # (inverse(denom, Q) * nom) % Q
# needs a little bit more work
# divide by gcd
= //
= //
= //
# baseline solution
= * %
# check in solutions of the shape (denum/gcd)*res = (num/gcd) mod (Q/gcd) + k * (Q/gcd) (k in [0, gcd[)
= +*
break
return
"""
Verifies a given set of g, h, p and x
:param g: Generator
:param h:
:param p: Prime
:param x: Computed X
:return:
"""
return ==
#generate a 1024 bit prime number for the Mod
#prime_mod = generate_probable_prime(1024)
=
#Generate Secret Exponent
= 0
=
=
#Generate the Transmit key A = G^x % P
=
return ,
#user1_public_key, user1_private_key = generate_key_pair(prime=3875972899)
, =
=
#Found Possible answer: 6244664414481744603
#Validates: True
#[Finished in 1461.2s]
Link to this headingPollards Kangaroo
- O(sqrt(b-a))
- can only be used when know some information about one of the private keys
Link to this headingPolhig-Hellman
- O(sqrt(p*))
- Can be used when n is factorable into many small primes
https://github.com/ashutosh1206/Crypton/tree/master/Discrete-Logarithm-Problem/Algo-Pohlig-Hellman
Link to this headingCyclic Groups
Link to this headingTelegram Implementation
The Server sends a nonce with the Key Derivation. This Nonce can be used to manipulate the output of the shared key.
Making it possible for the Server to manipulate the keys for each user.
Key Derivation with a Nonce?:
(g^a)^b mod p XOR nonce